/*
 * $Id: ASCTextFileDataItem.java,v 1.2 2009/06/22 15:32:14 afaichun Exp $
 * 
 * Copyright (C) 2008 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.common.util.dataItem;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.exceptions.BasicException;
import com.ge.healthcare.autosc.common.util.ExecResult;

/**
 * Implementation for accessing and updating a textual data item where
 * the data referenced by the data item is represented by a file.  Textual
 * data items are used to identify data in a manner analogous to
 * java.io.File objects.
 * @author 212042946
 *
 */
public class ASCTextFileDataItem implements IASCTextDataItem {
    /**
     * Path to the file containing the data represented by the data item.
     */
    private File theFile = null;

    /**
     * Construct a textual data item.
     *
     * @param helper Object providing the utility objects and methods.
     *
     * @param rootDirectory The directory which is the root of the directory
     *  tree containing the data represented by the data item.
     *
     * @param itemIdentifier The identifier of the item within the root
     *  directory.  This is the relative path, from the root directory, to
     *  the file containing the data represented by the data item.
     */
    public ASCTextFileDataItem( File rootDirectory,
                                    String itemIdentifier )
    {
        theFile = new File( rootDirectory, itemIdentifier );
    }

    /**
     * Get the file object for this data source
     * @return
     */
    public File getDataSourceFile() {
    	return theFile;
    }
    
    
    /**
     * Does the data identified by this data item exist?
     *
     * @return True if the data exists, false if not.
     */
    public boolean exists()
    {
        return theFile.exists();
    }

    /**
     * Delete the data identified by this data item.  Being unable to
     *  delete the data is considered an error, but not a fatal error.
     *  If the data does not exist, then the deletion is assumed to
     *  have succeeded.
     *
     * @return True if the data was deleted, false if not.
     */
    public boolean delete()
    {
        if ( !theFile.delete() )
        {
            return false;
        }
        return true;
    }

    /**
     * Open, for reading, the data identified by this data item.  It is
     *  the responsibility of the calling code to close the reader.
     *
     * @return Reader to use for reading the data.
     *
     * @exception AscServiceAbortException If unable to open the data.
     */
    public IASCTextReader openForReading() throws BasicException
    {
        InputStream rawSource = null;
        try
        {
            rawSource = new FileInputStream( theFile );
        }
        catch ( IOException e )
        {
            throw new BasicException("Unable to open " + theFile, e);
        }
        InputStream bufferedSource = new BufferedInputStream( rawSource );
        return new ASCSimpleTextReader( theFile.toString(), bufferedSource );
    }

    /**
     * Open, for writing, the data identified by this data item.  The
     *  existing data will be overwritten.  It is the responsibility
     *  of the calling code to close the writer.
     *
     * @return Writer to use for writing the data.
     *
     * @exception AscServiceAbortException If unable to open the data.
     */
    public PrintWriter openForWriting() throws BasicException {
        return openForWriting( false );
    }

    /**
     * Open, for writing, the data identified by this data item.  It is
     *  the responsibility of the calling code to close the writer.
     *
     * @param append True if appending to the data, false if the data should be
     *   overwritten.
     *
     * @return Writer to use for writing the data.
     *
     * @exception AscServiceAbortException If unable to open the data.
     */
    public PrintWriter openForWriting( boolean append )
        throws BasicException {
        String myPath = theFile.getPath();
        FileOutputStream rawStream = null;
        try
        {
            rawStream = new FileOutputStream( myPath, append );
        }
        catch ( IOException e )
        {
            throw new BasicException("Unable to open " + myPath + " for writing", e);
        }
        return new PrintWriter( new BufferedOutputStream( rawStream ) );
    }

    /**
     * Move the data to a different data item.  This moves the data from
     *  this data item to the destination data item.  If the destination
     *  data item contains existing data, the existing data is first
     *  deleted.  Once the move is complete, this data item will no longer
     *  contain any data.
     *
     * @param destination The data item to which this data item's data will
     *  be moved.
     *
     * @exception AscServiceAbortException If unable to move the data.
     */
    public void move( IASCTextDataItem destination )
        throws BasicException {
        ASCTextFileDataItem dest = castToSame( destination );
        String baseMessage = theFile + " to " + dest.theFile;
        dest.theFile.delete();
        if ( !theFile.renameTo( dest.theFile ) )
        {
        	throw new BasicException("Unable to move " + baseMessage);
        }
    }
    
    public void copy( IASCTextDataItem destination ) 
    	throws BasicException {
    	IASCTextReader reader = null;
    	PrintWriter writer = null;
    	try {
	    	ASCTextFileDataItem dest = castToSame( destination );
	    	dest.theFile.delete();
	    	reader = this.openForReading();
	    	writer = destination.openForWriting();
	    	for ( String currentLine = reader.readLine();
	        	  currentLine != null;
	        	  currentLine = reader.readLine() )
	    	{
	    		writer.println( currentLine );
	    	}
    	} finally {
    		reader.silentlyClose();
    		ASCUtil.silentlyClose(writer);
    	}
    }

    /**
     * Sort this data item's data, removing duplicates, and place the
     *  resulting data in the destination data item.  Any existing data
     *  in the destination data item is overwritten.  This data item's
     *  data remains unchanged.
     *
     * @param destination The data item where the results of the sort will
     *  be stored.
     *
     * @exception AscServiceAbortException If unable to sort the data or
     *  write it to the destination data item.
     */
    public void sortAndRemoveDuplicates( IASCTextDataItem destination )
        throws BasicException
    {
        ASCTextFileDataItem dest = castToSame( destination );
        dest.theFile.delete();
        String sortCommand = "sort -u " + theFile + " > " + dest.theFile;
        ExecResult sortResult = null;
        sortResult = ASCUtil.runShellCommand( sortCommand, new String[0] );
        if ( sortResult.getExitValue() != 0 )
        {
            StringBuffer msg = new StringBuffer().append("Sort of ")
                                 .append(theFile).append(" to ").append(dest.theFile)
                                 .append(" failed, result = ").append(sortResult.getExitValue())
                                 .append("{details:\n");
            String[] std = sortResult.getStdError();
            for(int i=0;i<std.length;i++) {
            	msg.append(std[i]).append("\n");
            }
            throw new BasicException(msg.toString());
        }
    }

    /**
     * Cast the specified data item to an object of the class defining
     *  this method.
     *
     * @param dataItem The data item being cast.
     *
     * @return The data item cast to the appropriate type.
     *
     * @exception AscServiceAbortException If a class cast exception is
     *  detected.
     */
    private ASCTextFileDataItem castToSame( IASCTextDataItem dataItem )
        throws BasicException
    {
        ASCTextFileDataItem castedItem = null;
        try
        {
            castedItem = (ASCTextFileDataItem) dataItem;
        }
        catch ( ClassCastException e )
        {
        	throw new BasicException("Invalid data item type", e);
        }
        return castedItem;
    }

    /**
     * Returns a string representation of the object.
     *
     * @return A string representation of the object.
     */
    public String toString()
    {
        return theFile.toString();
    }
}
